﻿package editor 
{	
	import org.audiofx.mp3.*;
	
	import flash.display.MovieClip;
	import flash.text.TextField;	
	import fl.controls.Button;	
	import fl.containers.ScrollPane;	
	
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import flash.net.FileReference;	
	import flash.net.FileFilter;		
	
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.display.DisplayObject;
	import flash.utils.ByteArray;	
	import flash.display.Sprite;
	
	
	public class Editor extends MovieClip 	
	{		
		//Sound loading
		private var FFile:FileReference;
		private var FSoundLoader:MP3FileReferenceLoader;
		private var FMP3Fiter:FileFilter;
		private var FSound:Sound;
		private var FChannel:SoundChannel;
		//XML loading and saving
		private var FXML:XML;
		private var FXMLSaver:FileReference;
		private var FXMLLoader:FileReference;
		private var FXMLFiter:FileFilter;
		private var FCurrentFile:String;
		//Interface
		private var FLoadSoundButton:Button;
		private var FPlayPauseButton:Button;
		private var FSaveXMLButton:Button;
		private var FLoadXMLButton:Button;		
		private var FStopButton:Button;
		private var FGenerateButton:Button;
		private var FWaveformContent:MovieClip;		
		private var FPlotter:WavePlotter;
		private var FCurrentTime:TextField;
		private var FTotalTime:TextField;
		private var FRatioText:TextField;
		private var FFileNameText:TextField;
		private var FFileBitrateText:TextField;
		private var FFileSizeText:TextField;
		private var FSkipText:TextField;
		
		private var FMarkTrackText:TextField;
		private var FMarkKeyText:TextField;
		private var FMarkYText:TextField;
		private var FMarkTimeText:TextField;
		
		private var FLengthText:TextField;
		private var FMarkHeightText:TextField;	
		
		private var FScroll:ScrollPane;
		private var FTrack:Track;
		
		public function Editor() 
		{
			FXML = <SoundData></SoundData>;
			FFile = new FileReference();
			FXMLSaver = new FileReference();
			FXMLLoader = new FileReference();
			FFile.addEventListener(Event.SELECT, OnSoundSelect);
			FXMLLoader.addEventListener(Event.SELECT, OnXMLSelect);
			FXMLLoader.addEventListener(Event.COMPLETE, OnXMLLoaded);
			FMP3Fiter = new FileFilter('MP3 File', '*.mp3');			
			FXMLFiter = new FileFilter('XML File', '*.xml');
			//interface setup
			Init();					
		}
		
		private function Init():void
		{
			//Sound
			FLoadSoundButton = getChildByName('LoadSoundButton') as Button;
			FLoadSoundButton.addEventListener(MouseEvent.MOUSE_DOWN, OnLoadSoundButton);
			FPlayPauseButton = getChildByName('PauseButton') as Button;
			FPlayPauseButton.addEventListener(MouseEvent.MOUSE_DOWN, OnPlayPauseButton);
			FStopButton = getChildByName('StopButton') as Button;
			FStopButton.addEventListener(MouseEvent.MOUSE_DOWN, OnStopButton);
			//XML
			FSaveXMLButton = getChildByName('SaveXMLButton') as Button;
			FSaveXMLButton.addEventListener(MouseEvent.MOUSE_DOWN, SaveXML);
			FLoadXMLButton = getChildByName('LoadXMLButton') as Button;
			FLoadXMLButton.addEventListener(MouseEvent.MOUSE_DOWN, LoadXML);
			//Generator			
			FGenerateButton = getChildByName('GenerateButton') as Button;
			FGenerateButton.addEventListener(MouseEvent.MOUSE_DOWN, RandomGeneration);			
			
			FWaveformContent = getChildByName('WaveformContent') as MovieClip;
			FPlotter = new WavePlotter(FWaveformContent.width, FWaveformContent.height);
			FPlotter.x = FWaveformContent.x;
			FPlotter.y = FWaveformContent.y;			
			FPlotter.addEventListener(FPlotter.SOUND_COMPLETE, OnSoundComplete);
			addChild(FPlotter);
			
			FCurrentTime = getChildByName('TimeText') as TextField;
			FTotalTime = getChildByName('TotalTimeText') as TextField;
			FRatioText = getChildByName('ScrollRatioText') as TextField;
			FFileNameText = getChildByName('FileNameText') as TextField;
			FFileBitrateText = getChildByName('FileBitrateText') as TextField;
			FFileSizeText = getChildByName('FileSizeText') as TextField;
			FSkipText = getChildByName('SkipText') as TextField;
			FMarkTrackText = getChildByName('MarckTrackText') as TextField;
			FMarkKeyText = getChildByName('MarkKeyText') as TextField;
			FMarkTimeText = getChildByName('MarkTimeText') as TextField;			
			FMarkYText = getChildByName('MarkYText') as TextField;						
			FLengthText = getChildByName('LengthText') as TextField;		
			FMarkHeightText = getChildByName('MarkHeightText') as TextField;			
			FSkipText.restrict = '0-9';			
			FScroll = getChildByName('TrackScroll') as ScrollPane;
			FScroll.addEventListener(Event.SCROLL, OnScroll);
			FTrack = new Track();
		}
		
		private function OnLoadSoundButton(e:MouseEvent):void
		{			
			FFile.browse([FMP3Fiter]);			
		}
		
		private function OnPlayPauseButton(e:MouseEvent):void
		{
			if(FPlotter.Playing)
			{
				FPlotter.Pause();
				FPlayPauseButton.label = 'Play';
			}
			else
			{
				FPlotter.Play();
				FPlayPauseButton.label = 'Pause';
			}
		}
		
		private function OnStopButton(e:MouseEvent):void
		{						
			FPlayPauseButton.label = 'Play';			
			FPlotter.Stop();
			OnPlotterMouseDown(null);
		}
		
		private function OnSoundSelect(e:Event):void
		{
			removeEventListener(Event.ENTER_FRAME, OnEnterFrame);			
			FCurrentTime.text = '00:00';
			FTotalTime.text = '00:00';
			
			SwitchButtons(false);
			FPlayPauseButton.label = 'Play';
			
			FSoundLoader = new MP3FileReferenceLoader();
			FSoundLoader.addEventListener(MP3SoundEvent.COMPLETE, OnSoundParsingComplete);
			FSoundLoader.getSound(FFile);
			FFileNameText.text = FFile.name;
			FCurrentFile = FFile.name;
			FFileSizeText.text = (FFile.size / (1024 * 1024)).toString().substring(0, 5) + ' MB';
		}
		
		private function OnSoundParsingComplete(e:MP3SoundEvent):void
		{
			FSound = e.sound;
			FFileBitrateText.text = Math.round(FSound.bytesTotal / FSound.length * 8).toString() + ' kbps';	
			GenerateWaveForm();
		}
		
		private function OnTags(e:Event):void
		{
			trace(FSound.id3.artist + ' - ' + FSound.id3.songName);
		}
		
		private function GenerateWaveForm():void
		{			
			FPlotter.Sound = FSound;						
			FPlotter.addEventListener(Event.COMPLETE, OnPlottingComplete);
		}
		
		private function OnPlotterMouseDown(e:MouseEvent):void
		{
			if(!FPlotter.Playing)
				FScroll.verticalScrollPosition = (FTrack.height - FScroll.height) * (1 - FPlotter.Position);
		}
		
		private function OnScroll(e:Event):void
		{			
			if(!FPlotter.Playing)
				FPlotter.Position = 1 - FScroll.verticalScrollPosition / (FTrack.height - FScroll.height); 
		}
		
		private function OnPlottingComplete(e:AnalyzeCompleteEvent):void
		{
			SwitchButtons(true);
			FTrack = new Track();		
			FTotalTime.text = FPlotter.TotalTime;
			FTrack.Time = Math.round(FSound.length);
			FTrack.Ratio = Number(FRatioText.text);
			FTrack.MarkHeight = Math.max(Number(FMarkHeightText.text), FTrack.MARK_HEIGHT_ACTUAL);
			FTrack.Generate();
			FScroll.source = FTrack;
			addEventListener(Event.ENTER_FRAME, OnEnterFrame);
			FPlotter.addEventListener(MouseEvent.MOUSE_DOWN, OnPlotterMouseDown);
			OnPlotterMouseDown(null);			
			FLengthText.text = FTrack.height.toString();
			FMarkHeightText.text = Math.max(Number(FMarkHeightText.text), FTrack.MARK_HEIGHT_ACTUAL).toString();
		}
		
		private function SwitchButtons(AState:Boolean):void
		{
			FPlayPauseButton.enabled = AState;
			FLoadXMLButton.enabled = AState;
			FStopButton.enabled = AState;
			FSaveXMLButton.enabled = AState;
			FGenerateButton.enabled = AState;
		}
		
		private function OnEnterFrame(e:Event):void
		{
			FCurrentTime.text = FPlotter.Time;			
			if(FPlotter.Playing)
			{
				FScroll.verticalScrollPosition = (FTrack.height - FScroll.height) * (1 - FPlotter.Position);
			}
		}
		
		private function OnSoundComplete(e:Event):void
		{
			FPlayPauseButton.label = 'Play';
		}
		
		public function UpdateTrackInfo(AMark:Sprite):void
		{
			FMarkTrackText.text = String(AMark.x / 26);
			FMarkKeyText.text = FTrack.KEYS[AMark.x / 26];
			FMarkTimeText.text = Math.round((FTrack.height - AMark.y - FTrack.TRESHHOLD) / FTrack.height * FSound.length) + ' msec';			
			FMarkYText.text = AMark.y.toString() + 'px';			
		}
		
		// ========================================= RANDOM ==========================================
		
		private function RandomGeneration(e:MouseEvent):void
		{
			FTrack = new Track();
			FTrack.Time = Math.round(FSound.length);
			FTrack.Ratio = Number(FRatioText.text);
			FTrack.MarkHeight = Math.max(Number(FMarkHeightText.text), FTrack.MARK_HEIGHT_ACTUAL);
			FTrack.Randomize(FPlotter.Amplitudes, int(FSkipText.text));
			FScroll.source = FTrack;			
			OnPlotterMouseDown(null);
			FLengthText.text = FTrack.height.toString();
			FMarkHeightText.text = Math.max(Number(FMarkHeightText.text), FTrack.MARK_HEIGHT_ACTUAL).toString();
		}
		
		// =========================================== XML ===========================================
		
		private function SaveXML(e:Event):void
		{
			FXML = FTrack.XMLStructure(FCurrentFile, FFileNameText.text);
			var vData:ByteArray = new ByteArray();
			vData.writeUTFBytes(FXML);
			FXMLSaver.save(vData, 'SoundData.xml');
		}
		
		private function LoadXML(e:Event):void
		{
			FXMLLoader.browse([FXMLFiter]);
		}	
		
		private function OnXMLSelect(e:Event):void
		{
			FXMLLoader.load();
		}
		
		private function OnXMLLoaded(e:Event):void
		{
			if(FSound)
			{
				removeEventListener(Event.ENTER_FRAME, OnEnterFrame);
				FPlotter.removeEventListener(MouseEvent.MOUSE_DOWN, OnPlotterMouseDown);
				
				FTrack = new Track();
				FPlayPauseButton.enabled = true;
				FStopButton.enabled = true;			
				FTrack.Time = Math.round(FSound.length);			
				var vXML:XML = XML(FXMLLoader.data.toString());
				FTrack.LoadFromXML(vXML);				
				FScroll.source = FTrack;								
				addEventListener(Event.ENTER_FRAME, OnEnterFrame);				
				FPlotter.addEventListener(MouseEvent.MOUSE_DOWN, OnPlotterMouseDown);
				OnPlotterMouseDown(null);
				
				FMarkHeightText.text = FTrack.MarkHeight.toString();
				FLengthText.text = FTrack.height.toString();
				FRatioText.text = FTrack.Ratio.toString().slice(0, 5);
			}
		}
	}		
}
